package com.hitqz.robot.driver.netty.handler;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.hitqz.robot.api.common.dto.robot.RobotOnlineEventDto;
import com.hitqz.robot.api.common.dto.robot.RobotPosInfoDto;
import com.hitqz.robot.api.common.dto.tickets.CtrlTicketsEvent;
import com.hitqz.robot.api.common.dto.warning.AddWarningDto;
import com.hitqz.robot.api.common.util.CacheNameUtil;
import com.hitqz.robot.driver.dto.map.MapDataDto;
import com.hitqz.robot.driver.dto.robot.*;
import com.hitqz.robot.driver.schedule.CheckAliveSchedule;
import com.hitqz.robot.driver.service.NettyChannelService;
import com.hitqz.robot.driver.util.RobotErrorEnum;
import com.hitqz.robot.driver.util.RobotStatusUtil;
import com.hitqz.robot.driver.util.SeqUtil;
import com.hitqz.robot.driver.util.SourceEnum;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.Lock;

/**
 * @author xupkun
 * @date 2023/8/4
 */
@Slf4j
@ChannelHandler.Sharable
public class RobotControllerMessageHandler extends ChannelInboundHandlerAdapter {

    //private static Lock lock =

    private SourceEnum sourceEnum;

    public RobotControllerMessageHandler(SourceEnum source) {
        this.sourceEnum = source;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyChannelService.addChannel(sourceEnum.getSource(), ctx);
        if(sourceEnum ==SourceEnum.ROBOT_STATUS) {
            CompletableFuture.runAsync(()->  {
                NettyChannelService.changeRobotOnline(0);
            });
        }
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("channelInactive: {}", sourceEnum.getSource());
        NettyChannelService.removeChannel(sourceEnum, ctx);
        super.channelInactive(ctx);
        // 重新连接
        NettyChannelService.connect(CheckAliveSchedule.ip, sourceEnum);
        if (sourceEnum == SourceEnum.ROBOT_STATUS) {
            CompletableFuture.runAsync(()-> {
                RobotOnlineEventDto robotChangeOnlineStatusDto = new RobotOnlineEventDto();
                robotChangeOnlineStatusDto.setOnlineStatus(0);
                SpringUtil.getApplicationContext().publishEvent(robotChangeOnlineStatusDto);
            });
            log.info("连接断开:" + sourceEnum.getSource());
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        CompletableFuture.runAsync(()->{
            MessageRobotDto dto = (MessageRobotDto) msg;
            String data = dto.getData().trim();
            Lock lock = null;
            InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
            String ip = address.getAddress().getHostAddress();

            switch(dto.getCmd()) {
                case 19301:
                    // 主动推送内容
                    Cache<String, RobotPushDto> cache = SpringUtil.getBean(CacheNameUtil.CACHE_ROBOT_INFO);
                    RobotPushDto pushDto  = JSONObject.parseObject(data, RobotPushDto.class);
                    RobotPushDto lastPushDto = Optional.ofNullable(cache.getIfPresent(ip)).orElse(new RobotPushDto());
                    cache.put(ip, pushDto);
                    Cache<String, RobotPosInfoDto> posInfoDtoCache = SpringUtil.getBean(CacheNameUtil.CACHE_POS_INFO);
                    RobotPosInfoDto infoDto  = JSONObject.parseObject(data, RobotPosInfoDto.class);
                    posInfoDtoCache.put(ip, infoDto);
                    if (pushDto.getTaskStatus() == 5 || pushDto.getTaskStatus() == 6 || pushDto.getTaskStatus() == 4) {
                        RobotStatusUtil.TARGET_POS_NAME =  "";
                    }
                    if (!Objects.equals(pushDto.getWarnings(),lastPushDto.getWarnings())) {
                        parseWarning(pushDto.getWarnings() , 3);
                    }
                    if (!Objects.equals(pushDto.getFatals(),lastPushDto.getFatals())) {
                        parseWarning(pushDto.getFatals() , 0);
                    }
                    if (!Objects.equals(pushDto.getErrors(),lastPushDto.getErrors())) {
                        parseWarning(pushDto.getErrors(), 2);
                    }
                    if (!Objects.equals(pushDto.getNotices(),lastPushDto.getNotices())) {
                        parseWarning(pushDto.getNotices() ,1);
                    }

                    // 项目要求，屏幕上面触发进行停止任务
                    Optional<RobotDIODto> di = pushDto.getDO().stream().filter(t-> t.getId().equals(9)).findFirst();
                    if (di.isPresent() && di.get().isStatus()) {
                        log.info("任务暂停的信号触发");
                        try {
                            NettyChannelService.sendMessage(SourceEnum.ROBOT_NAV.getSource(),
                                    SeqUtil.nextId(), 0x0BBB, null);
                            CtrlTicketsEvent ctrlTicketsEvent = new CtrlTicketsEvent();
                            ctrlTicketsEvent.setType(CtrlTicketsEvent.TYPE_STOP);
                            ctrlTicketsEvent.setMessage("上位机急停中断任务");
                            ctrlTicketsEvent.setTicketsId(0);
                            SpringUtil.getApplicationContext().publishEvent(ctrlTicketsEvent);
                            NettyChannelService.sendMessage(SourceEnum.ROBOT_OTHER.getSource(),
                                    SeqUtil.nextId(), 6001, "{\"id\":9,\"status\":false}".getBytes());
                        } catch (Exception e) {
                            log.info("解析外部停止信号发生异常:" + e.getMessage());
                        }
                    }
                    //log.info("接收到的状态数据是: {}", data);
                    break;
                case 11021:
                    lock = RobotStatusUtil.ROBOT_STATUS_RELOC_LOCK;
                    JSONObject jsonObject = JSONObject.parseObject(data);
                    log.info("ROBOT_STATUS_RELOC_LOCK:" + data);
                    if (jsonObject.containsKey("reloc_status")) {
                        RobotStatusUtil.ROBOT_RELOC_STATUS = jsonObject.getInteger("reloc_status");
                    }
                    break;
                case 11027:
                    lock = RobotStatusUtil.ROBOT_QUERY_JACK_STATUS_LOCK;
                    jsonObject = JSONObject.parseObject(data);
                    Integer jackStatus = jsonObject.getInteger("jack_state");
                    RobotStatusUtil.LIFT_STATUS = jackStatus;
                    break;
                case 11301:
                    RobotQueryStationsRetDto queryStationsRetDto  = JSONObject.parseObject(data, RobotQueryStationsRetDto.class);
                    lock = RobotStatusUtil.ROBOT_QUERY_MAP_POS;
                    log.info("queryStationsRetDto:" + data);
                    RobotStatusUtil.ROBOT_STATIONS = queryStationsRetDto;
                    break;
                case 11303:
                    RobotQueryPathDto robotQueryPathDto  = JSONObject.parseObject(data, RobotQueryPathDto.class);
                    log.info("path" + JSONObject.toJSONString(robotQueryPathDto));
                    RobotStatusUtil.ROBOT_QUERY_PATH_DATA = robotQueryPathDto;
                    lock = RobotStatusUtil.ROBOT_QUERY_PATH;
                    break;
                case 11300:
                    RobotQueryMapDto robotQueryMapDto = JSONObject.parseObject(data, RobotQueryMapDto.class);
                    RobotStatusUtil.ROBOT_QUERY_MAP_DATA = robotQueryMapDto;
                    lock = RobotStatusUtil.ROBOT_QUERY_MAP;
                    log.info("ROBOT_QUERY_MAP" + data);
                    break;
                case 14011:
                    // 从机器人下载地图, 实际上只能获取到地图上面的数据
                    lock = RobotStatusUtil.ROBOT_QUERY_MAP_ATTRIBUTES;
                    RobotStatusUtil.ROBOT_QUERY_ATTRIBUTES_DATA = JSONObject.parseObject(data, MapDataDto.class);
                    break;
                case 13051:
                    log.info("发送前往目标点结果:" + data);
                    jsonObject = JSONObject.parseObject(data);
                    RobotStatusUtil.ROBOT_GO_TARGET_RESULT = jsonObject.getInteger("ret_code") == 0;
                    lock = RobotStatusUtil.ROBOT_GO_TARGET_POS;
                    break;
                default:
                    //log.info("other command result: {} and result {} ", dto.getCmd(), data);
                    break;
            }
            if (lock != null) {
                synchronized (lock) {
                    try {
                        lock.notifyAll();
                    } catch (Exception e) {
                        log.error("释放锁失败:{}", e.getMessage());
                    }
                }
            }
        });
        super.channelRead(ctx, msg);
    }
  // 43.32
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        NettyChannelService.removeChannel(sourceEnum,  ctx);
        // 重新连接
        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        String ip = remoteAddress.getAddress().getHostAddress();
        log.info("连接断开" + sourceEnum.getSource() + "ip: " + ip);
        super.userEventTriggered(ctx, evt);
        NettyChannelService.connect(ip, sourceEnum);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (cause instanceof IOException) {
            log.info("可能是网路断开链接触发异常信息:");
            super.exceptionCaught(ctx, cause);
        }

    }



    private void parseWarning(String warningObj, Integer level) {
        if (StrUtil.isBlank(warningObj)) {
            return;
        }
        JSONArray jsonArray = JSONArray.parseArray(warningObj);
        if (!jsonArray.isEmpty()) {
            log.info("错误信息的数据:" + warningObj);
            int size = jsonArray.size();
            for (int i = 0; i < size; i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String code = extractCode(jsonObject);
                String content = extractContent(jsonObject);
                String suggestion = "请联系管理员，或者运维人员排查。";
                AddWarningDto addWarningDto = new AddWarningDto();
                addWarningDto.setCode(code);
                addWarningDto.setTitle("车辆报警");
                log.info("报警码是:" + code);
                RobotErrorEnum errorEnum = RobotErrorEnum.ERRORS.get(code);
                if (errorEnum != null) {
                    content = errorEnum.getMessage();
                    suggestion = errorEnum.getSuggestion();
                    addWarningDto.setContent(content);
                    addWarningDto.setSuggestion(suggestion);
                    addWarningDto.setLevel(level);
                    addWarningDto.setStopTask(errorEnum.isStopTask());
                    SpringUtil.getApplicationContext().publishEvent(addWarningDto);
                    // 清除报警warning
                    customSendMessage(SourceEnum.ROBOT_CONFIG.getSource(), 0x0FA9, null);
                } else {
                    log.info("出现其他不重要的故障: {}---{}", code, content);
                }
            }
        }
    }

    private String extractCode(JSONObject jsonObject) {
        String code = null;
        for (String key : jsonObject.keySet()) {
            if (isErrorCodeKey(key)) {
                code =  extractValue(jsonObject, key);
                break;
            }
        }
        return code;
    }

    private boolean isErrorCodeKey(String key) {
        return key.equals("code") ||
                (!Objects.equals(key, "dateTime") &&
                        !Objects.equals(key, "times") &&
                        !Objects.equals(key, "desc") && !Objects.equals(key, "reason"));
    }

    private String extractValue(JSONObject jsonObject, String key) {
        try {
            return jsonObject.getString(key);
        } catch (JSONException e) {
            log.warn("解析错误码时出现异常: key=" + key + ", value=" + jsonObject.get(key), e);
            return null;  // 或者抛出异常取决于业务需求
        }
    }

    private String extractContent(JSONObject jsonObject) {
        Object descValue = jsonObject.get("desc");
        if (descValue instanceof String) {
            String content = (String) descValue;
            if (content.contains("]")) {
                String[] parts = content.split("]");
                int index = Math.min(0, parts.length);
                return index == 0 ? content : parts[1];
            }
            return content;
        }
        return "";
    }


    public void customSendMessage(String sourceEnum, Integer cmd, Map params) {
        byte[] data = null;
        if (ObjectUtil.isNotEmpty(params)){
            data = new byte[10];
            data = JSONObject.toJSONString(params).getBytes(StandardCharsets.UTF_8);
        }
        NettyChannelService.sendMessage(sourceEnum,
                SeqUtil.nextId(), cmd, data);
    }
}
